package org.cainiao.io.nio.channel.file.map;

import java.io.File;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class MapFile {

    public static void main(String[] argv) throws Exception {
        // 创建一个临时文件，获取一个连接到该文件的通道
        File tempFile = File.createTempFile("mmaptest", null);
        RandomAccessFile randomAccessFile = new RandomAccessFile(tempFile, "rw");
        FileChannel channel = randomAccessFile.getChannel();
        // 从位置 0 开始向文件写入一些内容
        ByteBuffer temp = ByteBuffer.allocate(100);
        temp.put("This is the file content".getBytes());
        temp.flip();
        channel.write(temp, 0);
        /*
         * 从位置 8192 开始向文件中写入一些其它内容
         * 8192 为 8 KB，基本可以肯定会来到一个不同的“内存/文件系统”页
         * 根据文件系统页的大小，这可能会导致产生一个文件空洞
         * 来到一个不同的“内存/文件系统”页可以保证在此位置之后的写可以不受对前边页的 copy on write 复制的影响
         */
        temp.clear();
        temp.put("This is more file content".getBytes());
        temp.flip();
        channel.write(temp, 8192);
        // 创建到同一个文件的 3 种类型的映射
        MappedByteBuffer ro = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
        MappedByteBuffer rw = channel.map(FileChannel.MapMode.READ_WRITE, 0, channel.size());
        MappedByteBuffer cow = channel.map(FileChannel.MapMode.PRIVATE, 0, channel.size());
        // 发生任何变化前的缓冲区状态
        System.out.println("Begin");
        showBuffers(ro, rw, cow);
        // 修改 copy-on-write 缓冲区
        cow.position(8);
        cow.put("COW".getBytes());
        System.out.println("Change to COW buffer");
        showBuffers(ro, rw, cow);
        // 修改 read/write 缓冲区
        rw.position(9);
        rw.put(" R/W ".getBytes());
        rw.position(8194);
        rw.put(" R/W ".getBytes());
        rw.force();
        System.out.println("Change to R/W buffer");
        showBuffers(ro, rw, cow);
        // Write to the file through the channel; hit both pages
        temp.clear();
        temp.put("Channel write ".getBytes());
        temp.flip();
        channel.write(temp, 0);
        temp.rewind();
        channel.write(temp, 8202);
        System.out.println("Write on channel");
        showBuffers(ro, rw, cow);
        // 再次修改 copy-on-write 缓冲区
        cow.position(8207);
        cow.put(" COW2 ".getBytes());
        System.out.println("Second change to COW buffer");
        showBuffers(ro, rw, cow);
        // 修改 read/write 缓冲区
        rw.position(0);
        rw.put(" R/W2 ".getBytes());
        rw.position(8210);
        rw.put(" R/W2 ".getBytes());
        rw.force();
        System.out.println("Second change to R/W buffer");
        showBuffers(ro, rw, cow);
        // cleanup
        channel.close();
        randomAccessFile.close();
        tempFile.delete();
    }

    // Show the current content of the three buffers
    public static void showBuffers(ByteBuffer ro, ByteBuffer rw, ByteBuffer cow) {
        dumpBuffer("R/O", ro);
        dumpBuffer("R/W", rw);
        dumpBuffer("COW", cow);
        System.out.println();
    }

    // Dump buffer content, counting and skipping nulls
    public static void dumpBuffer(String prefix, ByteBuffer buffer) {
        System.out.print(prefix + ": '");
        int nulls = 0;
        int limit = buffer.limit();
        for (int i = 0; i < limit; i++) {
            char c = (char) buffer.get(i);
            if (c == '\u0000') {
                nulls++;
                continue;
            }
            if (nulls != 0) {
                System.out.print("|[" + nulls + " nulls]|");
                nulls = 0;
            }
            System.out.print(c);
        }
        System.out.println("'");
    }

}
